#include "lib/drivers/pvmdriver_tizen.h"
#include "lib/drivers/pvmdeviceinterface.h"
#include "lib/pvmerrors.h"
#include "lib/helpers/pvmoutstreamhelpers.h"
#include "lib/pvmstrings.h"
#include "lib/pvmdriver.h"
#include "lib/pvmlog.h"

#include "pvm/pvmhelperapi.h"

#include <FSysSystemTime.h>
#include <FBase.h>
#include <FMedia.h>
#include <FBaseByteBuffer.h>
#include <FSysSystemInfo.h>

#include "lib/memory/pvmmemory.h"

#define PVMDTIZEN_WAIT_FOR_SUSPEND		1000
#define PVMDTIZEN_DEFAULT_BUFFER_LENGTH	512
#define PVMDTIZEN_DEVICE_TYPE	EPVM_DTYPE_OUTPUT|EPVM_DTYPE_STREAM_WRITER

static int defaultDeviceVolume = 50;

using namespace Tizen::Base;
using namespace Tizen::Base::Collection;
using namespace Tizen::Base::Runtime;
using namespace Tizen::Media;


class PVMDeviceTizen : public IPVMDeviceOutSimple, public Tizen::Media::IAudioOutEventListener
{
public:
	PVMDeviceTizen(PVMDriverTizen* driver);
	~PVMDeviceTizen();

	virtual pvm_uint32 GetDeviceType();
	virtual void DeviceClose();
	virtual pvm_bool DeviceOpened();

	virtual pvm_bool SetOption(const pvm_char* optionName, void* param, pvm_uint32 paramSize);
	virtual pvm_bool GetOption(const pvm_char* optionName, void* param, pvm_uint32* paramSize);

	virtual void Suspend();
	virtual pvm_bool IsSuspended();
	virtual void Resume();

	virtual void DeviceNotify();

	virtual void SetBufferLengthMs(pvm_uint32 bufferSize);
	virtual pvm_uint32 GetBufferRealLengthMs();

	virtual pvm_int32 GetCPUUsage();

	virtual pvm_uint32 GetChanelsNum();
	virtual pvm_uint32 GetBitsPerSample();
	virtual pvm_uint32 GetSamplesPerSecond();

	pvm_bool Init(pvm_uint32 samplesPerSec, pvm_uint32 bitsPerSample, pvm_uint32 chanelsNum, pvm_int32 deviceNum, void *additionalInfo, pvm_uint32 infoSize, IPVMDeviceOutSimpleWaveSource *waveSource, pvm_bool deviceSuspended);

	virtual IPVMDriverBase* GetDriver() {return driver;}
protected:
	void DeInit();

	static PVMThreadRetVal PVMCALL PlaybackThreadProc(void* lpParameter);

	void PlaybackLoop();
	bool ResizeRenderBuffer(int newLengthMs);
	void OnInitFailed();

    void OnAudioOutBufferEndReached(Tizen::Media::AudioOut& src);
	void OnAudioOutInterrupted(Tizen::Media::AudioOut& src);
	void OnAudioOutReleased(Tizen::Media::AudioOut& src);

	pvm_bool isDeviceInitialized;

	PVMCriticalSection pvmdGlobalVarGuard;

	pvm_bool runThread;
	pvm_bool isThreaRunning;

	pvm_bool pauseRendering;
	pvm_bool isRenderingPaused;

	pvm_uint32			currentSampleRate;
	pvm_uint32			currentbitsPerSample;
	pvm_uint32			currentbytesPerSample;
	pvm_uint32			currentChanelsNum;

	//rendering sources
	pvm_uint32 currentBufferLength;
	pvm_uint32 newBufferLength;

	pvm_uint32 renderBufferSize;
	pvm_uint32 renderBufferSizeInSamples;
	pvm_uint32 renderBufferSizeInMS;
	pvm_uint32 suspendTimeoutMs;
	unsigned char* renderBuffer;

	PVMThreadHandle hPlayThread;
	PVMEventHandle hPlayEvent;

	IPVMDeviceOutSimpleWaveSource *waveSource;
	AudioOut* 	audioOutInstance;
	ArrayList* deviceBuffersArray;
	int scheduledBuffersCount;
	int nextFreeBuffer;
	int buffersPlayedTotal;
	PVMDriverTizen* driver;
};

pvm_uint32 PVMDeviceTizen::GetChanelsNum()
{
	return currentChanelsNum;
}

pvm_uint32 PVMDeviceTizen::GetBitsPerSample()
{
	return currentbitsPerSample;
}

pvm_uint32 PVMDeviceTizen::GetSamplesPerSecond()
{
	return currentSampleRate;
}

pvm_int32 PVMDeviceTizen::GetCPUUsage()
{
	return -1;
}

PVMDeviceTizen::PVMDeviceTizen(PVMDriverTizen* driver)
{
	isDeviceInitialized = false;

	//threading vars
	PVMInitThreadVar(&hPlayThread);

	runThread = false;
	isThreaRunning = false;

	pauseRendering = false;
	isRenderingPaused = false;

	currentSampleRate = 0;
	currentbitsPerSample = 0;
	currentbytesPerSample = 0;
	currentChanelsNum = 0;

	currentBufferLength = 0;
	newBufferLength = PVMDTIZEN_DEFAULT_BUFFER_LENGTH;

	renderBufferSize = 0;
	renderBufferSizeInSamples = 0;
	renderBufferSizeInMS = 0;
	suspendTimeoutMs = 0;
	renderBuffer = NULL;

	audioOutInstance = null;
	deviceBuffersArray = null;
	scheduledBuffersCount = 0;
	nextFreeBuffer = 0;

	waveSource = NULL;

	buffersPlayedTotal = 0;

	this->driver = driver;
}

PVMDeviceTizen::~PVMDeviceTizen()
{
	DeInit();
}

pvm_bool PVMDeviceTizen::Init(pvm_uint32 samplesPerSec, pvm_uint32 bitsPerSample, pvm_uint32 chanelsNum, pvm_int32 deviceNum, void *additionalInfo, pvm_uint32 infoSize, IPVMDeviceOutSimpleWaveSource *waveSource, pvm_bool deviceSuspended)
{
	PVMInitCritical(&pvmdGlobalVarGuard);

	PVMEnterCritical(&pvmdGlobalVarGuard);
	
	isDeviceInitialized = false;
	runThread = true;
	isThreaRunning = false;

	pauseRendering = deviceSuspended;
	isRenderingPaused = false;	

	PVMInitEvent(&hPlayEvent);

	PVMInitThreadVar(&hPlayThread);
	if(!PVMCreateThread(&hPlayThread, PVMDeviceTizen::PlaybackThreadProc, (void*)this))
	{
		PVMLeaveCritical(&pvmdGlobalVarGuard);
		PVMDeinitCritical(&pvmdGlobalVarGuard);
		PVMDeinitEvent(&hPlayEvent);
		return false;
	}

	PVMLeaveCritical(&pvmdGlobalVarGuard);

	if(!PVMIsThreadValid(hPlayThread))
	{
		PVMDeinitCritical(&pvmdGlobalVarGuard);
		PVMDeinitEvent(&hPlayEvent);
		return false;
	}

	PVMEnterCritical(&pvmdGlobalVarGuard);

	currentSampleRate = samplesPerSec;
	currentbitsPerSample = bitsPerSample;
	currentbytesPerSample = bitsPerSample/8;
	currentChanelsNum = chanelsNum;

	audioOutInstance = new AudioOut();

	if(audioOutInstance==null)
	{
		PVMLeaveCritical(&pvmdGlobalVarGuard);
		PVMDeinitCritical(&pvmdGlobalVarGuard);
		PVMDeinitEvent(&hPlayEvent);
		OnInitFailed();
		return false;
	}

	if(audioOutInstance->Construct(*this)!=E_SUCCESS)
	{
		PVMLeaveCritical(&pvmdGlobalVarGuard);
		PVMDeinitCritical(&pvmdGlobalVarGuard);
		PVMDeinitEvent(&hPlayEvent);
		OnInitFailed();
		return false;
	}

	AudioSampleType audioSampleType;
	AudioChannelType audioChannelType;

	switch(bitsPerSample)
	{
	case 8:
		audioSampleType = AUDIO_TYPE_PCM_U8;
		break;
	case 16:
		audioSampleType = AUDIO_TYPE_PCM_S16_LE;
		break;
	default:
		{
			PVMLeaveCritical(&pvmdGlobalVarGuard);
			PVMDeinitCritical(&pvmdGlobalVarGuard);
			PVMDeinitEvent(&hPlayEvent);
			OnInitFailed();
		}; break;
	}

	switch(chanelsNum)
	{
	case 1:
		audioChannelType = AUDIO_CHANNEL_TYPE_MONO;
		break;
	case 2:
		audioChannelType = AUDIO_CHANNEL_TYPE_STEREO;
		break;
	default:
		{
			PVMLeaveCritical(&pvmdGlobalVarGuard);
			PVMDeinitCritical(&pvmdGlobalVarGuard);
			PVMDeinitEvent(&hPlayEvent);
			OnInitFailed();
		}; break;
	}

	if(audioOutInstance->Prepare(audioSampleType, audioChannelType, samplesPerSec)!=E_SUCCESS)
	{
		PVMLeaveCritical(&pvmdGlobalVarGuard);
		PVMDeinitCritical(&pvmdGlobalVarGuard);
		PVMDeinitEvent(&hPlayEvent);
		OnInitFailed();
		return false;
	}

	Tizen::Base::String platformString(64);
	if(Tizen::System::SystemInfo::GetValue(L"PlatformVersion", platformString)==E_SUCCESS)
	{
		if(platformString.StartsWith(String(L"1.1"), 0))
		{
			defaultDeviceVolume = 99;
		}
	}

	audioOutInstance->SetVolume(defaultDeviceVolume);

	if(audioOutInstance->Start()!=E_SUCCESS)
	{
		PVMLeaveCritical(&pvmdGlobalVarGuard);
		PVMDeinitCritical(&pvmdGlobalVarGuard);
		PVMDeinitEvent(&hPlayEvent);
		OnInitFailed();
		return false;
	}

	isDeviceInitialized = true;

	this->waveSource = waveSource;

	PVMLeaveCritical(&pvmdGlobalVarGuard);
	DeviceNotify();

	return true;
}

void PVMDeviceTizen::DeInit()
{
	PVMSysLogOutString(_PVM_STR("PVMDeviceTizen::DeInit - enter"));
	if(isDeviceInitialized)
	{
		PVMEnterCritical(&pvmdGlobalVarGuard);
		runThread  = false;
		PVMLeaveCritical(&pvmdGlobalVarGuard);


		if(isRenderingPaused)
		{
			PVMSysLogOutString(_PVM_STR("PVMDeviceTizen::DeInit - resuming"));
			Resume();
		}

		while(isThreaRunning)
		{
			PVMSleep(10);
		}
	}

	if(DeviceOpened())
	{
		if(audioOutInstance!=null)
		{
			audioOutInstance->Stop();
			audioOutInstance->Unprepare();
			delete audioOutInstance;
			audioOutInstance = null;
		}
	}

	if(isDeviceInitialized)
	{
		PVMEnterCritical(&pvmdGlobalVarGuard);
		isDeviceInitialized = false;

		PVMLeaveCritical(&pvmdGlobalVarGuard);

		PVMDeinitEvent(&hPlayEvent);
		PVMDeinitCritical(&pvmdGlobalVarGuard);
	}

	if(deviceBuffersArray!=null)
	{
		deviceBuffersArray->RemoveAll(true);
		delete deviceBuffersArray;
		deviceBuffersArray = null;
	}

	if(renderBuffer!=NULL)
	{
		delete[] renderBuffer;
		renderBuffer = NULL;
	}
	PVMSysLogOutString(_PVM_STR("PVMDeviceTizen::DeInit - exit"));
	waveSource = NULL;
}

void PVMDeviceTizen::OnInitFailed()
{
	runThread  = false;
	if(isRenderingPaused)
		Resume();

	while(isThreaRunning)
	{
		PVMSleep(10);
	};

	if(audioOutInstance!=null)
	{
		audioOutInstance->Stop();
		audioOutInstance->Unprepare();
		delete audioOutInstance;
		audioOutInstance = null;
	}

	isDeviceInitialized = false;

	if(deviceBuffersArray!=null)
	{
		deviceBuffersArray->RemoveAll(true);
		delete deviceBuffersArray;
		deviceBuffersArray = null;
	}

	if(renderBuffer!=NULL)
	{
		delete[] renderBuffer;
		renderBuffer = NULL;
	}

	waveSource = NULL;
}

pvm_uint32 PVMDeviceTizen::GetDeviceType()
{
	return PVMDTIZEN_DEVICE_TYPE;
}

void PVMDeviceTizen::DeviceClose()
{
	DeInit();
	delete this;
}

pvm_bool PVMDeviceTizen::DeviceOpened()
{
	return isDeviceInitialized;
}

pvm_bool PVMDeviceTizen::SetOption(const pvm_char* optionName, void* param, pvm_uint32 paramSize)
{
	return false;
}

pvm_bool PVMDeviceTizen::GetOption(const pvm_char* optionName, void* param, pvm_uint32* paramSize)
{
	return false;
}

void PVMDeviceTizen::Suspend()
{
	if(pauseRendering&&pauseRendering)
		return;
	
	PVMSysLogOutString(_PVM_STR("PVM::Suspend"));

	PVMEnterCritical(&pvmdGlobalVarGuard);
	pauseRendering = true;
	PVMLeaveCritical(&pvmdGlobalVarGuard);

	pvm_uint32 time1 = PVMGetMilliSecondsCount();
	pvm_uint32 time2;
	if(PVMIsThreadValid(hPlayThread))
	{
		while(!isRenderingPaused)
		{
			time2 = PVMGetMilliSecondsCount();
			if(time2-time1>PVMDTIZEN_WAIT_FOR_SUSPEND)
				break;
			//check for long overflow - will no hang but will wait more than WAIT_FOR_SUSPEND
			if(time1>time2)
				time1 = time2;

			PVMSleep(1);
		}
	}
}

pvm_bool PVMDeviceTizen::IsSuspended()
{
	return isRenderingPaused&&pauseRendering;
}

void PVMDeviceTizen::Resume()
{
	PVMSysLogOutString(_PVM_STR("DManager::Resume()"));

	PVMEnterCritical(&pvmdGlobalVarGuard);
	pauseRendering = false;
	PVMLeaveCritical(&pvmdGlobalVarGuard);

	if(isRenderingPaused)
	{

		PVMEnterCritical(&pvmdGlobalVarGuard);
		isRenderingPaused = false;
		PVMLeaveCritical(&pvmdGlobalVarGuard);
		PVMSignalEvent(&hPlayEvent);
	}
}

void PVMDeviceTizen::DeviceNotify()
{
	if(!pauseRendering&&isRenderingPaused)
	{
		//PVMEnterCritical(&globalVarGuard);
		isRenderingPaused = false;
		//PVMLeaveCritical(&globalVarGuard);
		if(PVMIsThreadValid(hPlayThread))
			PVMSignalEvent(&hPlayEvent);
	}
}

void PVMDeviceTizen::SetBufferLengthMs(pvm_uint32 bufferSize)
{
	newBufferLength = bufferSize;
	DeviceNotify();
}

pvm_uint32 PVMDeviceTizen::GetBufferRealLengthMs()
{
	return currentBufferLength;
}

PVMThreadRetVal PVMCALL PVMDeviceTizen::PlaybackThreadProc(void* lpParameter)
{
	((PVMDeviceTizen*)lpParameter)->PlaybackLoop();
	return 0;
}

void PVMDeviceTizen::PlaybackLoop()
{
	PVMSysLogOutString(_PVM_STR(" PVMDeviceTizen::PlaybackLoop() - enter"));
	isThreaRunning = true;
	bool needSuspndThread = false;
	pvm_uint32 lastWriteSuccessTime = PVMGetMilliSecondsCount();
	//bool isWaveOutError = false;

	while(runThread)
	{
		needSuspndThread = false;

		if(waveSource==NULL || waveSource->IsInitialized()==false || audioOutInstance==null)
			needSuspndThread = true;
		else if((pauseRendering&&!isRenderingPaused)||(!isDeviceInitialized)||!waveSource->HasMoreData())
			needSuspndThread = true;

		pvm_uint32 currentMS = PVMGetMilliSecondsCount();
		bool suspendTimeout = (lastWriteSuccessTime + suspendTimeoutMs < currentMS);
		if(needSuspndThread && suspendTimeout)
		{
			result r;
			PVMSysLogOutString(_PVM_STR(" PVMDeviceTizen::PlaybackLoop() - suspending thread"));
			PVMEnterCritical(&pvmdGlobalVarGuard);
			isRenderingPaused = true;
			needSuspndThread = false;
			if(audioOutInstance!=null)
				r = audioOutInstance->Stop();
			PVMLeaveCritical(&pvmdGlobalVarGuard);
			PVMWaitForEvent(&hPlayEvent);

			if(audioOutInstance!=null)
				r = audioOutInstance->Start();
			else
				continue;
			PVMSysLogOutString(_PVM_STR(" PVMDeviceTizen::PlaybackLoop() - thread unsuspended"));
		}
//		else if(!suspendTimeout)
//		{
//			PVMSysLogOutString(_PVM_STR(" PVMDeviceTizen::PlaybackLoop() - suspend skipped"));
//		}



		if((currentBufferLength!=newBufferLength || deviceBuffersArray==null) && (waveSource!=NULL) && (waveSource->IsInitialized()!=false))
		{
			if(!ResizeRenderBuffer(newBufferLength))
			{
				//handle error
			}
			newBufferLength = currentBufferLength;
		}


		if(isDeviceInitialized && waveSource!=NULL && waveSource->HasMoreData() && deviceBuffersArray!=null && scheduledBuffersCount < deviceBuffersArray->GetCount() && renderBuffer!=NULL)
		{
			pvm_uint32 samplesFilled = 0;
			pvm_int renderBufferSizeInMS = (renderBufferSizeInSamples/currentChanelsNum)*1000/currentSampleRate;
			//long long startTime, endTime;
			//Tizen::System::SystemTime::GetTicks(startTime);
			samplesFilled = waveSource->FillBuffer(renderBuffer, renderBufferSizeInSamples);

			if(samplesFilled>0)
			{
				ByteBuffer* deviceBuffer = static_cast<ByteBuffer*>(deviceBuffersArray->GetAt(nextFreeBuffer));
				deviceBuffer->Rewind();
				deviceBuffer->SetArray(renderBuffer, 0, renderBufferSize);

				if(audioOutInstance->WriteBuffer(*deviceBuffer)==E_SUCCESS)
				{
//					PVMSysLogOutString(_PVM_STR(" PVMDeviceTizen::PlaybackLoop()  - audioOutInstance->WriteBuffer SUCCESS"));
					scheduledBuffersCount++;
					nextFreeBuffer++;
					if(nextFreeBuffer>=deviceBuffersArray->GetCount())
						nextFreeBuffer = 0;
					buffersPlayedTotal++;

					lastWriteSuccessTime = PVMGetMilliSecondsCount();
				}
				else
				{
					PVMSysLogOutString(_PVM_STR(" PVMDeviceTizen::PlaybackLoop()  - audioOutInstance->WriteBuffer ERROR"));
				}
			}

			//Tizen::System::SystemTime::GetTicks(endTime);
			//if(endTime - startTime > renderBufferSizeInMS)
			//{
			//	long long renderTime;
			//	renderTime = endTime - startTime;//renderBufferSizeInMS = renderBufferSizeInMS;
			//	renderTime = 0;
			//}
			//end of rendering
		}
		else
		{
			//Cache?
			//PVMEnterCritical(&pvmdRenderGuard);
			//PVMLeaveCritical(&pvmdRenderGuard);
			PVMSleep(1);
		}
		PVMSleep(1);
	}

	PVMEnterCritical(&pvmdGlobalVarGuard);
	isThreaRunning = false;
	pauseRendering = false;
	isRenderingPaused = false;
	PVMLeaveCritical(&pvmdGlobalVarGuard);

	PVMSysLogOutString(_PVM_STR(" PVMDeviceTizen::PlaybackLoop() - exit"));
}


bool PVMDeviceTizen::ResizeRenderBuffer(int newLengthMs)
{
	if(!isDeviceInitialized)
		return false;

	int minBufSizeInBytes = audioOutInstance->GetMinBufferSize();
	int maxBufSizeInBytes = audioOutInstance->GetMaxBufferSize();

	if(minBufSizeInBytes<0 || maxBufSizeInBytes<0)
		return false;

	PVMEnterCritical(&pvmdGlobalVarGuard);
	int sampleRate = currentSampleRate;
	int sampleSize = currentbytesPerSample;
	int chanelsNum = currentChanelsNum;
	PVMLeaveCritical(&pvmdGlobalVarGuard);

	int renderStepSize = sampleSize*chanelsNum;

	//default render buffer length is 20 MS
	int renderBufferSizeInBytes = sampleRate/50*renderStepSize;
	if(renderBufferSizeInBytes<minBufSizeInBytes)
	{
		renderBufferSizeInBytes = minBufSizeInBytes;//buffer too small for this hardware configuration
		if(renderBufferSizeInBytes%renderStepSize!=0)
			renderBufferSizeInBytes = (renderBufferSizeInBytes/renderStepSize + 1) * renderStepSize;
	}

	if(renderBufferSizeInBytes>maxBufSizeInBytes)
	{
		//buffer too large for this hardware configuration
		renderBufferSizeInBytes = maxBufSizeInBytes;
		if(renderBufferSizeInBytes%renderStepSize!=0)
			renderBufferSizeInBytes = (renderBufferSizeInBytes/renderStepSize - 1) * renderStepSize;
	}


	int newTotalLengthInBytes = (newLengthMs * currentbytesPerSample * chanelsNum * sampleRate) / 1000;
	int numDeviceBuffers = newTotalLengthInBytes / renderBufferSizeInBytes;

	if(numDeviceBuffers>64)
	{
		//Number of buffer vary large - try to reduce this number
		renderBufferSizeInBytes = newTotalLengthInBytes/64;
		if(renderBufferSizeInBytes>maxBufSizeInBytes)
		{
			renderBufferSizeInBytes = maxBufSizeInBytes;
			if(renderBufferSizeInBytes%renderStepSize!=0)
				renderBufferSizeInBytes = (renderBufferSizeInBytes/renderStepSize - 1) * renderStepSize;
		}
		else if(renderBufferSizeInBytes%renderStepSize!=0)
		{
			renderBufferSizeInBytes = (renderBufferSizeInBytes/renderStepSize + 1) * renderStepSize;
		}

		numDeviceBuffers = newTotalLengthInBytes / renderBufferSizeInBytes;
	}
	else if(numDeviceBuffers<6)
	{
		//Number of buffer too low - try to increase this number
			renderBufferSizeInBytes = newTotalLengthInBytes/6;
			if(renderBufferSizeInBytes>maxBufSizeInBytes)
			{
				renderBufferSizeInBytes = maxBufSizeInBytes;
				if(renderBufferSizeInBytes%renderStepSize!=0)
					renderBufferSizeInBytes = (renderBufferSizeInBytes/renderStepSize - 1) * renderStepSize;
			}
			else if(renderBufferSizeInBytes%renderStepSize!=0)
			{
				renderBufferSizeInBytes = (renderBufferSizeInBytes/renderStepSize + 1) * renderStepSize;
			}

			numDeviceBuffers = newTotalLengthInBytes / renderBufferSizeInBytes;
	}

	if(deviceBuffersArray==null)
	{
		deviceBuffersArray = new ArrayList();
		deviceBuffersArray->Construct();
	}

	if(deviceBuffersArray==null)
		return false;

	pvm_bool needToStartDevice = false;
	if(scheduledBuffersCount!=0)
	{
		audioOutInstance->Reset();
		needToStartDevice = true;
	}

	deviceBuffersArray->RemoveAll(true);
	nextFreeBuffer = 0;

	if(renderBuffer!=NULL)
	{
		delete[] renderBuffer;
		renderBuffer = NULL;
	}

	renderBuffer = new unsigned char [renderBufferSizeInBytes];
	if(renderBuffer==NULL)
		return false;

	for(int i=0; i<numDeviceBuffers; i++)
	{
		ByteBuffer* deviceBuffer = new ByteBuffer();
		deviceBuffer->Construct(renderBufferSizeInBytes);
		deviceBuffersArray->Add(*deviceBuffer);
	}


	renderBufferSize = renderBufferSizeInBytes;
	renderBufferSizeInSamples = renderBufferSize/sampleSize;
	currentBufferLength = (renderBufferSizeInSamples * numDeviceBuffers / chanelsNum)
							* 1000 / sampleRate;
	renderBufferSizeInMS = renderBufferSizeInSamples * 1000/ (chanelsNum * sampleRate);
	suspendTimeoutMs = renderBufferSizeInMS << 3;

	if(waveSource!=NULL)
		waveSource->ResizeRenderBuffer(renderBufferSize);

	if(needToStartDevice)
		audioOutInstance->Start();

	return true;
}

void PVMDeviceTizen::OnAudioOutBufferEndReached(Tizen::Media::AudioOut& src)
{
	if(scheduledBuffersCount>0)
	{
		scheduledBuffersCount--;
	}
	else
	{
		//some sort of error!
		int errnum = 0;
	}
}

void PVMDeviceTizen::OnAudioOutInterrupted(Tizen::Media::AudioOut& src)
{
	Suspend();
	//result r = src.Stop();
}

void PVMDeviceTizen::OnAudioOutReleased(Tizen::Media::AudioOut& src)
{
	Resume();
	result r = src.Start();
}

////////////////////////////////////////////////////////////////////////////////
//PVMDriverTizen															  //
////////////////////////////////////////////////////////////////////////////////

PVMDriverTizen::PVMDriverTizen()
{
}

PVMDriverTizen::~PVMDriverTizen()
{
}

pvm_bool PVMDriverTizen::Init()
{

	return true;
}

pvm_bool PVMDriverTizen::IsInitialized()
{
	return true;
}

void PVMDriverTizen::DriverClose()
{
	delete this;
}

pvm_uint32 PVMDriverTizen::GetDriverType()
{
	return PVMDTIZEN_DEVICE_TYPE;
}

pvm_bool PVMDriverTizen::GetDriverOption(const pvm_char* optionName, void* optionVal, pvm_uint32* optionSize)
{
	return false;
}

pvm_bool PVMDriverTizen::SetDriverOption(const pvm_char* optionName, void* optionVal, pvm_uint32 optionSize)
{
	return false;
}


pvm_uint32 PVMDriverTizen::DevicesNum()
{
	return 1;
}

pvm_int32 PVMDriverTizen::DeviceName(pvm_int32 nDevice, pvm_char* buffer, pvm_uint32 maxLength)
{
	if(buffer==0||maxLength==0)
		return 11;
	if(maxLength<11)
		return 0;
	pvm_strcpy(buffer, _PVM_STR("raw writer"));

	return pvm_strlen(buffer);
}

pvm_bool PVMDriverTizen::DeviceEnumModes(pvm_int32 nDeivce, TFormatEnumCallbackProc enumProc, void *param)
{
	if(!enumProc)
		return false;

	enumProc(1, 8, 11025, param);
	enumProc(1, 8, 22050, param);
	enumProc(1, 8, 44100, param);
	enumProc(1, 8, 48000, param);

	enumProc(1, 16, 11025, param);
	enumProc(1, 16, 22050, param);
	enumProc(1, 16, 44100, param);
	enumProc(1, 16, 48000, param);

	enumProc(2, 8, 11025, param);
	enumProc(2, 8, 22050, param);
	enumProc(2, 8, 44100, param);
	enumProc(2, 8, 48000, param);

	enumProc(2, 16, 11025, param);
	enumProc(2, 16, 22050, param);
	enumProc(2, 16, 44100, param);
	enumProc(2, 16, 48000, param);

	return true;
}

IPVMDeviceOutSimple* PVMDriverTizen::DeviceOpen(pvm_uint32 samplesPerSec, pvm_uint32 bitsPerSample, pvm_uint32 chanelsNum, pvm_int32 deviceNum, void *additionalInfo, pvm_uint32 infoSize, IPVMDeviceOutSimpleWaveSource *waveSource, pvm_bool deviceSuspended)
{
	if(!IsInitialized())
		return NULL;

	PVMDeviceTizen* device = new PVMDeviceTizen(this);
	if(!device)
		return NULL;
	if(!device->Init(samplesPerSec, bitsPerSample, chanelsNum, deviceNum, additionalInfo, infoSize, waveSource, deviceSuspended))
	{
		device->DeviceClose();
		return NULL;
	}

	return device;
}

void PVMDriverTizen::CloseDevice(IPVMDeviceBase* device)
{
	if(device!=NULL)
		device->DeviceClose();
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

IPVMDriverOut* PVMDriverCreateTizen()
{
	PVMDriverTizen* outDrv = new PVMDriverTizen();
	if(!outDrv)
		return NULL;
	PVMDriverSimple* simpledrv = new PVMDriverSimple(outDrv);
	if(!simpledrv)
	{
		outDrv->DriverClose();
	}

	return simpledrv;
}

static pvm_int32 DriverNameShort(pvm_char* buffer, pvm_uint32 maxLength)
{
	return PVMCopyDriverName(_PVM_STR("Tizen driver"), buffer, maxLength);
}

static pvm_int32 DriverNameFull(pvm_char* buffer, pvm_uint32 maxLength)
{
	return PVMCopyDriverName(_PVM_STR("Tizen driver"), buffer, maxLength);
}

static pvm_uint32 DriverTypeAndFlags()
{
	return PVMDTIZEN_DEVICE_TYPE;
}

static IPVMDriverBase* DriverLoad()
{
	return PVMDriverCreateTizen();
}

IPVMDriverDesc	TIZENDesc;

IPVMDriverDesc* PVMGetDriverDescTizen()
{
	TIZENDesc.DriverNameShort = DriverNameShort;
	TIZENDesc.DriverNameFull = DriverNameFull;
	TIZENDesc.DriverTypeAndFlags = DriverTypeAndFlags;
	TIZENDesc.DriverLoad = DriverLoad;
	return &TIZENDesc;
}

#undef	PVMDTIZEN_WAIT_FOR_SUSPEND
#undef	PVMDTIZEN_DEFAULT_BUFFER_LENGTH
#undef	PVMDTIZEN_DEVICE_TYPE
